//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_BYTE_AND_BIT_DIMENSIONS_H
#define _UXS_BYTE_AND_BIT_DIMENSIONS_H

#pragma once

#include "Helpers.h"

namespace UX_STL
{
namespace UXTypes
{
	
	template <typename T> struct TBytes;
	template <typename T> struct TBits;

	typedef TBytes<usize>	Bytes;
	typedef TBits<usize>	Bits;


	//
	// Bytes
	//

	template <typename T>
	struct TBytes : TypeDescriptor::MustBeInteger<T>
	{
	// types
	public:
		typedef TBytes<T>	Self;
		typedef T			value_t;


	// variables
	private:
		T	_value;


	// methods
	public:
		TBytes () : _value(0) {}

		explicit
		TBytes (T value) : _value(value) {}
		
		template <typename B>
		explicit
		TBytes (TBits<B> bits);

		TBits<T> ToBits () const;
		
		template <typename B>
		TBits<B> ToBits () const;

		operator T () const				{ return _value; }

		template <typename B>
		TBytes<B> To () const;

		T		Kb ()	const			{ return _value >> 10; }
		T		Mb ()	const			{ return _value >> 20; }
		T		Gb ()	const			{ return _value >> 30; }
		
		template <typename B>
		B		Kb ()	const			{ return B(_value) / (1 << 10); }

		template <typename B>
		B		Mb ()	const			{ return B(_value) / (1 << 20); }

		template <typename B>
		B		Gb ()	const			{ return B(_value) / (1 << 30); }

		Self &	FromKb (T value)		{ _value = (value << 10);  return *this; }
		Self &	FromMb (T value)		{ _value = (value << 20);  return *this; }
		Self &	FromGb (T value)		{ _value = (value << 30);  return *this; }

		template <typename B>
		static Self SizeOf ()			{ return Self( sizeof(B) ); }
		
		template <typename B>
		static Self SizeOf (B value)	{ return Self( sizeof(B) ); }

		string ToString () const;

		_UX_DIM_ALL_INTEGER_OPERATORS_SELF( _value )
	};



	//
	// Bits
	//

	template <typename T>
	struct TBits : TypeDescriptor::MustBeInteger<T>
	{
	// types
	public:
		typedef TBits<T>	Self;
		typedef T			value_t;


	// variables
	private:
		T	_value;
		

	// methods
	public:
		TBits () : _value(0) {}

		explicit
		TBits (T value) : _value(value) {}
		
		template <typename B>
		explicit
		TBits (TBytes<B> bytes);

		TBytes<T> ToBytes () const;
		
		template <typename B>
		TBytes<B> ToBytes () const;
		
		operator T () const				{ return _value; }
		
		template <typename B>
		TBits<B> To () const;

		template <typename B>
		static Self SizeOf ()			{ return TBytes<T>::SizeOf<B>().ToBits(); }
		
		template <typename B>
		static Self SizeOf (B value)	{ return TBytes<T>::SizeOf<B>().ToBits(); }
		
		string ToString () const;

		_UX_DIM_ALL_INTEGER_OPERATORS_SELF( _value )
	};



//----------------------------------- TBytes --------------------------------//
	
	template <typename T>
	template <typename B>
	inline TBytes<T>::TBytes (TBits<B> bits) : _value( T((B)bits) >> 3 )
	{}
	

	template <typename T>
	inline TBits<T>  TBytes<T>::ToBits () const
	{
		return TBits<T>( *this );
	}
	

	template <typename T>
	template <typename B>
	inline TBits<B>  TBytes<T>::ToBits () const
	{
		return TBits<B>( *this );
	}
	

	template <typename T>
	template <typename B>
	inline TBytes<B>  TBytes<T>::To () const
	{
		return TBytes<B>( (B)_value );
	}
	

	template <typename T>
	inline string TBytes<T>::ToString () const
	{
		const T	kb = T(1) << 14;
		const T mb = T(1) << 24;
		const T	gb = T(1) << UXMath::Min( (T)34, (T) TBits<T>::SizeOf<T>()-1 );

		if ( _value < kb )	return string().FormatI( _value, 3, ' ' ) << " b";
		if ( _value < mb )	return string().FormatI( Kb(),   3, ' ' ) << " Kb";
		if ( _value < gb )	return string().FormatI( Mb(),   3, ' ' ) << " Mb";
							return string().FormatI( Gb(),   3, ' ' ) << " Gb";
	}

		

//----------------------------------- TBits ---------------------------------//

	template <typename T>
	template <typename B>
	inline TBits<T>::TBits (TBytes<B> bytes) : _value( T((B)bytes) << 3 )
	{}
	

	template <typename T>
	inline TBytes<T>  TBits<T>::ToBytes () const
	{
		return TBytes<T>( *this );
	}
	

	template <typename T>
	template <typename B>
	inline TBytes<B>  TBits<T>::ToBytes () const
	{
		return TBytes<B>( *this );
	}
	

	template <typename T>
	template <typename B>
	inline TBits<B>  TBits<T>::To () const
	{
		return TBits<B>( (B) _value );
	}
	

	template <typename T>
	inline string TBits<T>::ToString () const
	{
		const T	kb = T(1) << 14;
		const T mb = T(1) << 24;
		const T	gb = T(1) << UXMath::Min( (T)34, (T) TBits<T>::SizeOf<T>()-1 );

		if ( _value < kb )	return string().FormatI( _value, 3, ' ' ) << " bit";
		if ( _value < mb )	return string().FormatI( Kb(),   3, ' ' ) << " Kbit";
		if ( _value < gb )	return string().FormatI( Mb(),   3, ' ' ) << " Mbit";
							return string().FormatI( Gb(),   3, ' ' ) << " Gbit";
	}


}	// UXTypes
}	// UX_STL

#endif	// _UXS_BYTE_AND_BIT_DIMENSIONS_H